#include "prec.h"
#include "gradientbutton.h"
#include "wx/dcbuffer.h"

IMPLEMENT_CLASS(GradientButton, wxControl)

BEGIN_EVENT_TABLE(GradientButton, wxControl)
	EVT_PAINT(GradientButton::OnPaint)
	EVT_ERASE_BACKGROUND(GradientButton::OnEraseBackground)
	EVT_SIZE(GradientButton::OnSize)
	EVT_LEFT_DOWN(GradientButton::OnLeftDown)
	EVT_LEFT_UP(GradientButton::OnLeftUp)
	EVT_LEAVE_WINDOW(GradientButton::OnMouseLeave)
	EVT_ENTER_WINDOW(GradientButton::OnMouseEnter)
	EVT_SET_FOCUS(GradientButton::OnGainFocus)
	EVT_KILL_FOCUS(GradientButton::OnLoseFocus)
	EVT_KEY_DOWN(GradientButton::OnKeyDown)
	EVT_KEY_UP(GradientButton::OnKeyUp)
	EVT_LEFT_DCLICK(GradientButton::OnLeftDown)
END_EVENT_TABLE()

static wxGDIImage* ConvertImage( const wxGDIImage& bitmap )
{
	bool isIcon = bitmap.IsKindOf( CLASSINFO(wxIcon) );

	if( !isIcon )
	{
		wxASSERT_MSG( wxDynamicCast(&bitmap, wxBitmap),
			_T("not an icon and not a bitmap?") );

		const wxBitmap& bmp = (const wxBitmap&)bitmap;
		wxMask *mask = bmp.GetMask();
		if ( mask && mask->GetMaskBitmap() )
		{
			wxIcon* icon = new wxIcon;
			icon->CopyFromBitmap(bmp);

			return icon;
		}

		return new wxBitmap( bmp );
	}

	// copying a bitmap is a cheap operation
	return new wxIcon( (const wxIcon&)bitmap );
}

GradientButton::GradientButton( wxWindow *parent, wxWindowID id, 
							   const wxGDIImage& bitmap, 
							   const wxString& label, 
							   const wxPoint& pos /*= wxDefaultPosition*/, 
							   const wxSize& size /*= wxDefaultSize*/, 
							   long style /*= wxNO_BORDER*/, 
							   const wxValidator& validator /*= wxDefaultValidator*/, 
							   const wxString& name /*= wxT("gradientbutton")*/ )
							   : wxControl(parent, id, pos, size, style, validator, name)
{
	m_mouseAction = 0;
	m_bitmap = ConvertImage(bitmap);
	m_hasFocus = false;

	SetLabel(label);
	InheritAttributes();
	SetInitialSize(size);

	m_bottomStartColour = *wxBLACK;

	unsigned char r,g,b,a;
	r = m_bottomStartColour.Red();
	g = m_bottomStartColour.Green();
	b = m_bottomStartColour.Blue();
	a = m_bottomStartColour.Alpha();

	m_bottomEndColour = LightColour(m_bottomStartColour, 20);
	m_topStartColour = LightColour(m_bottomStartColour, 40);
	m_topEndColour = LightColour(m_bottomStartColour, 25);
	m_pressedTopColour = LightColour(m_bottomStartColour, 20);
	m_pressedBottomColour = wxColour(r,g,b,a);
	SetForegroundColour(*wxWHITE);
}

GradientButton::GradientButton( wxWindow *parent, wxWindowID id, 
							   const wxString& label, 
							   const wxPoint& pos /*= wxDefaultPosition*/, 
							   const wxSize& size /*= wxDefaultSize*/, 
							   long style /*= wxNO_BORDER*/, 
							   const wxValidator& validator /*= wxDefaultValidator*/, 
							   const wxString& name /*= wxT("gradientbutton")*/ )
							   : wxControl(parent, id, pos, size, style, validator, name)
{
	m_mouseAction = 0;
	m_bitmap = NULL;
	m_hasFocus = false;

	SetLabel(label);
	InheritAttributes();
	SetInitialSize(size);

	m_bottomStartColour = *wxBLACK;

	unsigned char r,g,b,a;
	r = m_bottomStartColour.Red();
	g = m_bottomStartColour.Green();
	b = m_bottomStartColour.Blue();
	a = m_bottomStartColour.Alpha();

	m_bottomEndColour = LightColour(m_bottomStartColour, 20);
	m_topStartColour = LightColour(m_bottomStartColour, 40);
	m_topEndColour = LightColour(m_bottomStartColour, 25);
	m_pressedTopColour = LightColour(m_bottomStartColour, 20);
	m_pressedBottomColour = wxColour(r,g,b,a);
	SetForegroundColour(*wxWHITE);
}

GradientButton::~GradientButton()
{
	Free();
}

void GradientButton::OnPaint( wxPaintEvent& event )
{
	wxBufferedPaintDC dc(this);

	dc.SetBackground(wxBrush(GetParent()->GetBackgroundColour()));
	dc.Clear();

	wxRect clientRect = GetClientRect();
	wxRect gradientRect(clientRect);
	wxWindow* capture = wxWindow::GetCapture();

	int x = clientRect.x, y = clientRect.y, width = clientRect.width, height = clientRect.height;
	gradientRect.SetHeight(gradientRect.GetHeight()/2 + (capture==this ? 1 : 0));

	wxColour topStart = m_topStartColour, topEnd = m_topEndColour;

	if (capture != this)
	{
		if (m_mouseAction == HOVER)
		{
			topStart = LightColour(m_topStartColour, 10);
			topEnd = LightColour(m_topEndColour, 10);
		}

		wxRect rc1(x, y, width, height/2);
		dc.GradientFillLinear(rc1, topStart, topEnd, wxSOUTH);
		//dc.GradientFillLinear(wxRect(x, y+height/2-8, width, 8), topStart, topEnd, wxSOUTH);
	}
	else
	{
		wxRect rc1(x, y, width, height);
		dc.SetPen(wxPen(m_pressedTopColour));
		dc.SetBrush(wxBrush(m_pressedTopColour));
		dc.DrawRectangle(rc1);
	}

	gradientRect.Offset(0, gradientRect.GetHeight());

	wxColour bottomStart = m_bottomStartColour, bottomEnd = m_bottomEndColour;
	int shadowOffset;
	if (capture != this)
	{
		if (m_mouseAction == HOVER)
		{
			bottomStart = LightColour(m_bottomStartColour, 10);
			bottomEnd = LightColour(m_bottomEndColour, 10);
		}

		wxRect rc3(x, y+height/2, width, height/2);
		dc.GradientFillLinear(rc3, bottomStart, bottomEnd, wxSOUTH);
		//dc.GradientFillLinear(wxRect(x, y+height/2, width, 8), bottomStart, bottomEnd, wxSOUTH);

		shadowOffset = 0;
	}
	else
	{
		wxRect rc1(x+1, gradientRect.height/2, gradientRect.width, gradientRect.height);
		dc.SetPen(wxPen(m_pressedBottomColour));
		dc.SetBrush(wxBrush(m_pressedBottomColour));
		dc.DrawRectangle(rc1);

		shadowOffset = 1;
	}

	dc.SetFont(GetFont());
	dc.SetPen(wxPen(GetForegroundColour()));

	wxString label = GetLabel();
	wxSize tsize = dc.GetTextExtent(label);
	int tw = tsize.x, th = tsize.y;
	int bw = 0, bh = 0;
	if (m_bitmap)
	{
		bw = m_bitmap->GetWidth();
		bh = m_bitmap->GetHeight();
	}

	int pos_x = (width-bw-tw)/2+shadowOffset;
	if (m_bitmap)
	{
		int pos_y =  (height-bh)/2+shadowOffset;
		dc.DrawBitmap(*(wxBitmap*)m_bitmap, pos_x, pos_y);
		pos_x = pos_x + 2;
	}

	dc.DrawText(label, pos_x + bw + shadowOffset, (height-th)/2+shadowOffset);

}

void GradientButton::OnEraseBackground( wxEraseEvent& event )
{

}

void GradientButton::OnSize( wxSizeEvent& event )
{
	event.Skip();
	Refresh();
}

void GradientButton::OnLeftDown( wxMouseEvent& event )
{
	if(!IsEnabled())
		return;

	m_mouseAction = CLICK;
	CaptureMouse();
	Refresh();
	event.Skip();
}

void GradientButton::OnLeftUp( wxMouseEvent& event )
{
	if (!IsEnabled() || !HasCapture())
		return;

	wxPoint pos = event.GetPosition();
	wxRect rect = GetClientRect();

	if (HasCapture())
		ReleaseMouse();

	if (rect.Contains(pos))
	{
		m_mouseAction = HOVER;
		Notify();
	}
	else
	{
		m_mouseAction = 0;
	}

	Refresh();
	event.Skip();
}

void GradientButton::OnMouseLeave( wxMouseEvent& event )
{
	m_mouseAction = None;
	Refresh();
	event.Skip();
}

void GradientButton::OnMouseEnter( wxMouseEvent& event )
{
	if (!IsEnabled())
		return;

	m_mouseAction = HOVER;
	Refresh();
	event.Skip();
}

void GradientButton::OnGainFocus( wxFocusEvent& event )
{
	m_hasFocus = true;
	Refresh();
	Update();
}

void GradientButton::OnLoseFocus( wxFocusEvent& event )
{
	m_hasFocus = false;
	Refresh();
	Update();
}

void GradientButton::OnKeyDown( wxKeyEvent& event )
{
	if (m_hasFocus && event.GetKeyCode() == ' ')
	{
		m_mouseAction = HOVER;
		Refresh();
	}
	event.Skip();
}

void GradientButton::OnKeyUp( wxKeyEvent& event )
{
	if (m_hasFocus && event.GetKeyCode() == ' ')
	{
		m_mouseAction = HOVER;
		Notify();
		Refresh();
	}
	event.Skip();
}

void GradientButton::Free()
{
	if (m_bitmap)
	{
		delete m_bitmap;
		m_bitmap = NULL;
	}
}

wxColour GradientButton::LightColour( wxColour& colour, int percent )
{
	wxColour end_colour = *wxWHITE;
	int rd = end_colour.Red() - colour.Red();
	int gd = end_colour.Green() - colour.Green();
	int bd = end_colour.Blue() - colour.Blue();
	int high = 100;

	// We take the percent way of the colour from colour -. white
	int i = percent;
	int r = colour.Red() + ((i*rd*100)/high)/100;
	int g = colour.Green() + ((i*gd*100)/high)/100;
	int b = colour.Blue() + ((i*bd*100)/high)/100;

	return wxColour(r, g, b);
}

/*wxGraphicsPath GradientButton::GetPath( wxGraphicsContext* gc, const wxRect& rc, int r )
{
	wxGraphicsPath path = gc->CreatePath();
	path.AddRoundedRectangle(rc.x, rc.y, rc.width, rc.height, r);
	path.CloseSubpath();
	return path;
}*/

bool GradientButton::AcceptsFocus() const
{
	return IsShown() && IsEnabled();
}

wxVisualAttributes GradientButton::GetDefaultAttributes() const
{
	return wxButton::GetClassDefaultAttributes();
}

bool GradientButton::Enable( bool enable /*= true*/ )
{
	bool ret = wxControl::Enable(enable);
	Refresh();
	return ret;
}

bool GradientButton::SetForegroundColour( const wxColour& colour )
{
	bool ret = wxControl::SetForegroundColour(colour);
	Refresh();
	return ret;
}

wxSize GradientButton::DoGetBestSize() const
{
	wxString label = GetLabel();
	if (label.IsEmpty())
		return wxSize(112, 48);

	wxClientDC dc((wxWindow*)this);
	dc.SetFont(GetFont());
	wxSize size = dc.GetTextExtent(label);

	int bmpWidth = 0, bmpHeight = 0;
	int constant = 15;
	if (m_bitmap)
	{
		bmpWidth = m_bitmap->GetWidth()+10;
		bmpHeight = m_bitmap->GetHeight();
		size.x += bmpWidth;
		size.y = size.y > bmpHeight ? size.y : bmpHeight;
		constant = 15;
	}

	return wxSize(size.x+constant, size.y+constant);
}

void GradientButton::SetDefault()
{
	
}

void GradientButton::Notify()
{
	GradientButtonEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
	evt.SetButtonObj(this);
	evt.SetEventObject(this);
	GetEventHandler()->ProcessEvent(evt);
}

void GradientButton::SetTopStartColour( const wxColour& colour )
{
	m_topStartColour = colour;
	Refresh();
}

void GradientButton::SetTopEndColour( const wxColour& colour )
{
	m_topEndColour = colour;
	Refresh();
}

void GradientButton::SetBottomStartColour( const wxColour& colour )
{
	m_bottomStartColour = colour;
	Refresh();
}

void GradientButton::SetBottomEndColour( const wxColour& colour )
{
	m_bottomEndColour = colour;
	Refresh();
}

void GradientButton::SetPressedBottomColour( const wxColour& colour )
{
	m_pressedBottomColour = colour;
	Refresh();
}

void GradientButton::SetPressedTopColour( const wxColour& colour )
{
	m_pressedTopColour = colour;
	Refresh();
}